package com.udima.reglocation;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.TimeZone;
import java.util.Timer;
import java.util.TimerTask;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.annotation.SuppressLint;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;

import com.udima.reglocation.entities.UserSession;
import com.udima.reglocation.util.AsyncTaskCompleteListener;
import com.udima.reglocation.util.GPSTracker;
import com.udima.reglocation.util.HttpAsyncTask;
import com.udima.reglocation.util.PreferencesHandler;
import com.udima.reglocation.util.UserSessionHandler;

public class LocationService extends Service implements AsyncTaskCompleteListener {

	private static final String ACTION_PREFIX = "com.udima.reglocation.intentservice.action.";

	public static final String ACTION_LOCATION_SERVICE_COORDS = ACTION_PREFIX + "ACTION_LOCATION_SERVICE_COORDS";
	public static final String ACTION_LOCATION_SERVICE_SENDDATAPERIOD = ACTION_PREFIX + "ACTION_LOCATION_SERVICE_SENDDATAPERIOD";
	public static final String ACTION_LOCATION_SERVICE_GEOLOCATIONDISABLED = ACTION_PREFIX + "ACTION_LOCATION_SERVICE_GEOLOCATIONDISABLED";
	public static final String ACTION_LOCATION_SERVICE_USERSESSIONNOTPRESENT = ACTION_PREFIX + "ACTION_LOCATION_SERVICE_USERSESSIONNOTPRESENT";
	public static final String ACTION_LOCATION_SERVICE_NONRECOVERABLEERROR = ACTION_PREFIX + "ACTION_LOCATION_SERVICE_NONRECOVERABLEERROR";
	public static final String ACTION_LOCATION_SERVICE_CONNECTIONERROR = ACTION_PREFIX + "ACTION_LOCATION_SERVICE_CONNECTIONERROR";

	private static final int ID_NOTIFICACION_CREATE = 1;

	private NotificationManager nm;
	private GPSTracker gps;
	private Timer timer;
	private static SimpleDateFormat sdfMillis;

	private int sendDataPeriod;
	private boolean serviceRunning;
	private UserSession userSession;
	private JSONObject jsonUserSession;

	@Override
	public void onCreate() {
		nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
	}

	@Override
	@SuppressLint("SimpleDateFormat")
	public int onStartCommand(Intent intenc, int flags, int idArranque) {

		sdfMillis = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss,SSS");
		sdfMillis.setTimeZone(TimeZone.getDefault());

		// creamos el controlador de gps
		gps = new GPSTracker(LocationService.this);

		// comprobamos si la geolocalizacion está activada
		if (!gps.isCanGetLocation()) {
			Intent bcIntent = new Intent();
			bcIntent.setAction(ACTION_LOCATION_SERVICE_GEOLOCATIONDISABLED);
			sendBroadcast(bcIntent);
			this.stopSelf();
		} else {
			serviceRunning = true;
		}

		if (serviceRunning) {

			// notificación en la barra de acciones de servicio ejecutándose
			Notification notificacion = new Notification(R.drawable.ic_launcher, "Arrancando servicio " + getString(R.string.app_name),
					System.currentTimeMillis());
			PendingIntent pendIntent = PendingIntent.getActivity(this, 0, new Intent(this, SaveTrackActivity.class), 0);
			notificacion
					.setLatestEventInfo(this, "Servicio " + getString(R.string.app_name) + " arrancado", "Registrando posiciones gps", pendIntent);
			nm.notify(ID_NOTIFICACION_CREATE, notificacion);

			// Recuperación de la sesión de usuario
			UserSessionHandler ush = new UserSessionHandler(getApplicationContext());
			userSession = ush.retrieveUserSession();
			if (userSession == null) {
				// Comunicamos que no hay sesion de usuario servidor a la
				// actividad
				// padre
				Intent bcIntent = new Intent();
				bcIntent.setAction(ACTION_LOCATION_SERVICE_USERSESSIONNOTPRESENT);
				sendBroadcast(bcIntent);
			} else {
				jsonUserSession = userSessionToJson(userSession);
				if (jsonUserSession != null) {
					// recuperacion del tiempo de envio de datos al servidor y
					// arranque del envio de posiciones
					startSendingLocationData();
				}
			}
		}

		return START_STICKY;
	}

	@Override
	public void onDestroy() {
		serviceRunning = false;

		if (timer != null) {
			timer.cancel();
		}

		if (gps != null) {
			gps.stopUsingGPS();
		}

		if (nm != null) {
			nm.cancel(ID_NOTIFICACION_CREATE);
		}
	}

	@Override
	public IBinder onBind(Intent intent) {
		return null;
	}

	/**
	 * Transformación del objeto UserSession a un formato JSon
	 * 
	 * @param us
	 * @return
	 */
	private JSONObject userSessionToJson(UserSession us) {
		JSONObject res = null;

		res = new JSONObject();
		try {
			res.put("userId", us.getUserId());
			res.put("sessionToken", us.getSessionToken());
		} catch (JSONException e) {
			// Comunicamos el error a la actividad padre
			Intent bcIntent = new Intent();
			bcIntent.setAction(ACTION_LOCATION_SERVICE_NONRECOVERABLEERROR);
			bcIntent.putExtra("exception", e.getMessage());
			sendBroadcast(bcIntent);
		}

		return res;
	}

	/**
	 * Arranque del envío de datos al servidor después de recuperar el intervalo
	 * de tiempo del envío
	 */
	private void startSendingLocationData() {

		if (jsonUserSession == null) {
			// Comunicamos que no hay sesion de usuario servidor a la actividad
			// padre
			Intent bcIntent = new Intent();
			bcIntent.setAction(ACTION_LOCATION_SERVICE_USERSESSIONNOTPRESENT);
			sendBroadcast(bcIntent);
		} else {

			PreferencesHandler ph = new PreferencesHandler(getApplicationContext());
			String appBaseUrl = ph.retrieveAppBaseUrl();

			JSONObject jsonReq = new JSONObject();
			try {
				jsonReq.put("userSession", jsonUserSession);
				jsonReq.put("userId", userSession.getUserId());
			} catch (JSONException e) {
				// Comunicamos el error a la actividad padre
				Intent bcIntent = new Intent();
				bcIntent.setAction(ACTION_LOCATION_SERVICE_NONRECOVERABLEERROR);
				bcIntent.putExtra("exception", e.getMessage());
				sendBroadcast(bcIntent);
			}

			// Definición del callback particular para la recuperacion de datos
			// de usuario
			final AsyncTaskCompleteListener callback = new AsyncTaskCompleteListener() {

				@Override
				public void onTaskComplete(String result) {

					if (HttpAsyncTask.CONNECTIONERROR.equals(result)) {
						// Comunicamos el error de conexión a la actividad padre
						Intent bcIntent = new Intent();
						bcIntent.setAction(ACTION_LOCATION_SERVICE_CONNECTIONERROR);
						sendBroadcast(bcIntent);
					} else {
						try {
							JSONObject json = new JSONObject(result);

							if (json.getString("responseSuccessful") == "true") {

								JSONObject jsonUser = json.getJSONObject("user");
								String periodStr = jsonUser.getString("fetchGpsLapseTime");
								if (periodStr != null) {
									sendDataPeriod = Integer.parseInt(periodStr);
								}

								// Comunicamos el tiempo de envio de datos al
								// servidor a la actividad
								// padre
								Intent bcIntent = new Intent();
								bcIntent.setAction(ACTION_LOCATION_SERVICE_SENDDATAPERIOD);
								bcIntent.putExtra("sendDataPeriod", sendDataPeriod);
								sendBroadcast(bcIntent);

								// arrancamos el temporizador de envío de datos
								// al
								// servidor
								timer = new Timer();
								timer.scheduleAtFixedRate(new TimerTask() {
									public void run() {
										sendLocation();
									}
								}, 0, sendDataPeriod);

							} else {
								// Comunicamos el error a la actividad padre
								Intent bcIntent = new Intent();
								bcIntent.setAction(ACTION_LOCATION_SERVICE_NONRECOVERABLEERROR);
								sendBroadcast(bcIntent);
							}
						} catch (JSONException e) {
							// Comunicamos el error a la actividad padre
							Intent bcIntent = new Intent();
							bcIntent.setAction(ACTION_LOCATION_SERVICE_NONRECOVERABLEERROR);
							bcIntent.putExtra("exception", e.getMessage());
							sendBroadcast(bcIntent);
						}
					}
				}
			};

			// Ejecución de la consulta al servicio rest de lectura de usuario
			new HttpAsyncTask(this) {
				@Override
				protected void onPostExecute(String result) {
					super.onPostExecute(result);
					callback.onTaskComplete(result);
				}
			}.execute(appBaseUrl + "/rest/users", jsonReq.toString());

		}
	}

	/**
	 * Recoge la posición actual y la envía al servidor
	 * 
	 */
	@SuppressLint("SimpleDateFormat")
	private void sendLocation() {

		// Obtención de la posición actual del GPS
		double latitude = gps.getLatitude();
		double longitude = gps.getLongitude();

		// Comunicamos las coordenadas recuperadas a la
		// actividad padre
		Intent bcIntent = new Intent();
		bcIntent.setAction(ACTION_LOCATION_SERVICE_COORDS);
		bcIntent.putExtra("lat", latitude);
		bcIntent.putExtra("lon", longitude);
		sendBroadcast(bcIntent);

		// Creación y serialización de la cadena gpsData con los datos de envio
		// del GPS en formato json
		JSONObject jsonReq = new JSONObject();
		JSONArray jsonGpsDataArray = new JSONArray();
		JSONObject jsonGpsData = new JSONObject();
		try {
			jsonGpsData.put("userId", userSession.getUserId());
			jsonGpsData.put("fetchTime", Calendar.getInstance().getTimeInMillis());
			jsonGpsData.put("lat", String.valueOf(latitude));
			jsonGpsData.put("lon", String.valueOf(longitude));

			jsonReq.put("userSession", jsonUserSession);
			jsonGpsDataArray.put(jsonGpsData);
			jsonReq.put("gpsData", jsonGpsDataArray);
		} catch (JSONException e) {
			// Comunicamos el error a la actividad padre
			Intent erIntent = new Intent();
			erIntent.setAction(ACTION_LOCATION_SERVICE_NONRECOVERABLEERROR);
			erIntent.putExtra("exception", e.getMessage());
			sendBroadcast(erIntent);
		}

		PreferencesHandler ph = new PreferencesHandler(getApplicationContext());
		String appBaseUrl = ph.retrieveAppBaseUrl();

		// Ejecución del envio de datos del GPS al servidor sin callback
		HttpAsyncTask asyncTaskLocations = new HttpAsyncTask(this);
		asyncTaskLocations.execute(appBaseUrl + "/rest/gps/store/", jsonReq.toString());
	}

	@Override
	public void onTaskComplete(String result) {
	}
}
